/**
 * 用于提供标记线程信息相关的工具库
 */
import { WorkerTransmit, ActionMeta } from './worker_ds';
import { addTiggerAction, getWorkerInstance, getPort } from './worker_mgr';
import { isMainThread } from 'worker_threads';
import { BasicAction, WorkerIdentify } from './worker_define';

type ClassConstructor<T = any> = new (...args: any[]) => T;
type IntanceTarget<T = any> = { constructor: ClassConstructor<T> };
type ActionMetaExt<T = any> = {
  instance: IntanceTarget<T>;
  mate: ActionMeta;
};
/** 记录依赖信息 */
class Dep {
  constructor(
    public readonly propertyName = '',
    public readonly dependName = ''
  ) {}
}

/** 记录本线程内各个被注解的类的元信息 */
const CLASS_API_EXPORT_MAPPER = new Map<ClassConstructor, Set<ActionMeta>>();
/** 本地线程内，构造器与实例一一映射，有且只有一个实例 */
const CLASS_INSTANCE_MAPPER = new Map<ClassConstructor, Object>();
/** 本地线程内，类中需要暴露的成员变量 */
const CLASS_EXPORT_PROPERTY_MAPPER = new Map<ClassConstructor, Set<string>>();
/* 记录本地线程依赖注入的信息 */
const LOCAL_DEPENDENCY_INJECT = new Map<ClassConstructor, Array<Dep>>();

const LOCAL_EXPORT_INFO = new Array<ActionMeta>();

/** 记录本地所有需要依赖注入的类注册方法，将在线程激活前调用 */
export const LOCAL_INJECT_QUEUE = new Array<Function>();

/**
 * 获取当前进程的描述信息
 * @returns
 */
export function getWorkerInfo() {
  return LOCAL_EXPORT_INFO;
}


/**
 * 新增或更新暴露函数的信息
 * @param params
 * @param updateMeta
 */
function addOrUpdateAction<T>(
  params: {
    target: IntanceTarget<T>;
    prop: string;
  },
  updateMeta: Partial<ActionMeta>
) {
  const { target, prop } = params;
  const constructor = target.constructor;

  let actionSet = CLASS_API_EXPORT_MAPPER.get(constructor);
  if (!actionSet) {
    actionSet = new Set();
    CLASS_API_EXPORT_MAPPER.set(constructor, actionSet);
  }
  let mate = actionSet.values().find((item) => item.label === prop);
  if (!mate) {
    mate = new ActionMeta(prop);
    actionSet.add(mate);
  }
  Object.assign(mate, updateMeta);
}

function addTiggerActionExt(arr: Array<ActionMetaExt>) {
  for (const e of arr) {
    addTiggerAction(e.mate.event, (transmit: WorkerTransmit) => {
      // 动作已完成
      if (transmit.completeTime) {
        // 当前是主进程，且目标线程不是自己时仅转发
        if (!isMainThread) {
          throw new HydrangeaError(ErrorCode.BAD_CALL_FUNCTION, 'Action has been completed');
        }
      }
      // 非主线程接收到不属于自己的请求 - 应当是错误请求
      if (!isMainThread && transmit.dstIdentify !== process.identity) {
        throw new HydrangeaError(ErrorCode.BAD_CALL_FUNCTION, 'Passed to a non-target thread');
      }
      const args = Array.isArray(transmit.payload) ? transmit.payload : [transmit.payload];
      // 通过注解决定是否将传输对象结构传入
      const dto = e.mate.has_transmit ? [transmit] : args;
      const func = Reflect.get(e.instance, e.mate.label) as Function;
      // todo: 需要进一步的错误处理
      const result = Promise.resolve(func.call(e.instance, ...dto));
      return {
        ...e.mate,
        result
      };
    });
  }
}

/**
 * 使用一个入口实例，通过该类扫描所有暴露的函数
 * @param entry
 */
function exportAction(entry: IntanceTarget) {
  const baseConstructor = entry.constructor;
  const baseExports = CLASS_API_EXPORT_MAPPER.get(baseConstructor);
  if (!baseExports) {
    return;
  }
  const exportList = new Array<ActionMetaExt>();
  for (const property of baseExports.values()) {
    exportList.push({
      instance: entry,
      mate: property
    });
  }
  const propertyExports = CLASS_EXPORT_PROPERTY_MAPPER.get(baseConstructor);
  if (propertyExports) {
    for (const propertyName of propertyExports.values()) {
      const target = Reflect.get(entry, propertyName);
      const targetConstructor = target.constructor;
      const targetExports = CLASS_API_EXPORT_MAPPER.get(targetConstructor);
      if (!targetExports) {
        continue;
      }
      for (const innerProperty of targetExports.values()) {
        innerProperty.event = `${propertyName}.${innerProperty.label}`;
        exportList.push({
          instance: target,
          mate: innerProperty
        });
      }
    }
  }

  for (const e of exportList) {
    LOCAL_EXPORT_INFO.push(e.mate);
  }
  addTiggerActionExt(exportList);
  return LOCAL_EXPORT_INFO;
}


/**
 * 以原始的方式扩展本地线程函数暴露
 * 暴露将直接以函数名为action
 * @param entry 
 * @returns 
 */
export function extExportAction(entry: IntanceTarget) {
  const extConstructor = entry.constructor;
  const extExports = CLASS_API_EXPORT_MAPPER.get(extConstructor);
  if (!extExports) {
    return;
  }
  const exportList = new Array<ActionMetaExt>();
  for (const property of extExports.values()) {
    exportList.push({
      instance: entry,
      mate: property
    });
  }
  for (const e of exportList) {
    LOCAL_EXPORT_INFO.push(e.mate);
  }
  addTiggerActionExt(exportList);
}

/**
 * 标记一个函数是否有返回值
 * @param falg
 * @returns
 */
export function HasReturn(falg: boolean = true) {
  return function (target, prop) {
    addOrUpdateAction({ target, prop }, { has_return: falg });
  };
}

/**
 * 标记一个函数是否需要默认传输对象
 * @returns
 */
export function HasTransmit() {
  return function (target, prop) {
    addOrUpdateAction({ target, prop }, { has_transmit: true });
  };
}

/**
 * 标记一个函数的调用超时时间
 * @param timeout
 * @returns
 */
export function MaxTimeout(timeout: number = 1000) {
  return function (target, prop) {
    addOrUpdateAction({ target, prop }, { max_timeout: timeout });
  };
}

/**
 * 标记一个函数详细信息
 * @param desc
 * @returns
 */
export function FunctionDesc(desc: string) {
  return function (target, prop) {
    addOrUpdateAction({ target, prop }, { desc: desc });
  };
}

/**
 * 标记一个属性的导出
 */
export function ExportProperty() {
  return function (target: any, prop: string) {
    const TargetConstructor = target.constructor;
    let list = CLASS_EXPORT_PROPERTY_MAPPER.get(TargetConstructor);
    if (!list) {
      list = new Set();
      CLASS_EXPORT_PROPERTY_MAPPER.set(TargetConstructor, list);
    }
    list.add(prop);
  };
}

/**
 * 标记该属性将才有注入方式导入其他线程的引用
 */
export function InjectWorker(name: string): PropertyDecorator {
  return function <T extends { constructor: ClassConstructor }>(Module: T, prop: any) {
    const TargetConstructor = Module.constructor;
    notNil(TargetConstructor, 'TargetConstructor is not found');
    let dependList = LOCAL_DEPENDENCY_INJECT.get(TargetConstructor);
    if (!dependList) {
      dependList = new Array();
      LOCAL_DEPENDENCY_INJECT.set(TargetConstructor, dependList);
    }
    dependList.push({
      dependName: name,
      propertyName: prop
    });
  } as PropertyDecorator;
}

/**
 * 标记本区域内所有函数暴露
 * @returns
 */
export function ExportAll() {
  return function (m) {
    const prototype = m.isThread ? m.__proto__.prototype : m.prototype;
    const names = Object.getOwnPropertyNames(prototype).filter(
      (item) => item !== 'constructor' && typeof prototype[item] === 'function'
    );
    const targetConstructor = prototype.constructor;
    let metas = CLASS_API_EXPORT_MAPPER.get(targetConstructor);
    if (!metas) {
      metas = new Set();
      CLASS_API_EXPORT_MAPPER.set(targetConstructor, metas);
    }
    const exitting = metas.values();
    for (const name of names) {
      if (exitting.find((item) => item.label === name)) {
        continue;
      }
      metas.add(new ActionMeta(name));
    }
    return m;
  };
}

/**
 * 标记该类为线程启动类
 */
export function Thread() {
  return function <T extends ClassConstructor>(module: T) {
    const SubClass = class extends module {
      static get name() {
        return module.name;
      }
      static get isThread() {
        return true;
      }
      constructor(...args: any[]) {
        super(...args);
        if (CLASS_INSTANCE_MAPPER.get(module)) {
          throw new Error('Thread can only be used once');
        }
        // @ts-ignore
        this.constructor = module;
        CLASS_INSTANCE_MAPPER.set(module, this);

        Promise.resolve(this._register());
        LOCAL_INJECT_QUEUE.push(this._inject.bind(this));
      }
      /** 获取当前类实例所有函数并注册 */
      private _register() {
        const target = CLASS_INSTANCE_MAPPER.get(module) ?? this;
        exportAction(target as IntanceTarget);
        const info = LOCAL_EXPORT_INFO;
        const transmit = new WorkerTransmit(WorkerIdentify.MAIN_THREAD_IDENTIFY, BasicAction.ADD_META, info);
        if (process.identity != WorkerIdentify.MAIN_THREAD_IDENTIFY) {
          getPort(WorkerIdentify.MAIN_THREAD_IDENTIFY).postMessage(transmit);
        }
      }
      private async _inject() {
        const dependList = LOCAL_DEPENDENCY_INJECT.get(module);
        if (!dependList) {
          return;
        }
        const instance = CLASS_INSTANCE_MAPPER.get(module);
        if (!instance) {
          throw new HydrangeaError(ErrorCode.BAD_CALL_FUNCTION, 'Instance not found');
        }
        for (const depend of dependList) {
          const worker = await getWorkerInstance(depend.dependName);
          Reflect.set(this, depend.propertyName, worker);
        }
      }
    };

    return SubClass as T;
  };
}
